package com.ljc.websocket;

import com.alibaba.fastjson.JSONObject;
import com.ljc.entity.Danmu;
import com.ljc.service.DanmuService;
import com.ljc.utils.DanmuRabbitMQUtils;
import com.ljc.utils.TokenUtil;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Component
@ServerEndpoint("/imserver/{token}")
public class WebSocketService {
    // 存储每个客户端的WebSocketService
    public static final ConcurrentHashMap<String, WebSocketService> WEBSOCKET_MAP = new ConcurrentHashMap<>();
    // 统计在线人数
    private static final AtomicInteger ONLINE_COUNT = new AtomicInteger(0);
    private static ApplicationContext APPLICATION_CONTEXT;
    private Session session;
    private String sessionId;
    private Long userId;

    /**
     * static方法获取唯一的容器环境
     */
    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketService.APPLICATION_CONTEXT = applicationContext;
    }

    @OnOpen // @OnOpen表示当websocket连接是调用修饰的方法
    public void openConnection(Session session, @PathParam("token") String token) {
        try {
            this.userId = TokenUtil.verifyToken(token);
        } catch (Exception ignored) {
        }
        this.sessionId = session.getId();
        this.session = session;
        // map中已经保存过该sessionID表示之前连接过，进行对WebSocketService进行更新
        if (WEBSOCKET_MAP.containsKey(sessionId)) {
            WEBSOCKET_MAP.remove(sessionId);
            WEBSOCKET_MAP.put(sessionId, this);
        } else {
            // 之前没有连接过，记录到map中，并让在线人数+1
            WEBSOCKET_MAP.put(sessionId, this);
            ONLINE_COUNT.getAndIncrement();
        }
        log.info("用户连接成功：{}, 当前在线人数为：{}", sessionId, ONLINE_COUNT.get());
        try {
            this.sendMessage("0");
        } catch (Exception e) {
            log.error("连接异常");
        }
    }

    @OnClose // @OnClose表示客户端关闭页面调用的方法
    public void closeConnection() {
        if (WEBSOCKET_MAP.containsKey(sessionId)) {
            WEBSOCKET_MAP.remove(sessionId);
            ONLINE_COUNT.getAndDecrement();
        }
        log.info("用户退出：{}, 当前在线人数为：{}", sessionId, ONLINE_COUNT.get());
    }

    @OnMessage // @OnMessage表示消息交互时调用的方法
    public void onMessage(String message) {
        log.info("用户信息：{}, 报文：{}", sessionId, message);
        if (!StringUtil.isNullOrEmpty(message)) {
            try {
                //群发消息（当服务端拿到一个客户端发送的弹幕后，群发给所有客户端）
                for (Map.Entry<String, WebSocketService> entry : WEBSOCKET_MAP.entrySet()) {
                    WebSocketService webSocketService = entry.getValue();
                    DanmuRabbitMQUtils danmuRabbitMQUtils = (DanmuRabbitMQUtils) APPLICATION_CONTEXT.getBean("getDanmuRabbitMQUtils");
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("message", message);
                    jsonObject.put("sessionId", webSocketService.getSessionId());
                    danmuRabbitMQUtils.sendDanmuAsync(jsonObject);
                }
                if (this.userId != null) {
                    //保存弹幕到数据库
                    Danmu danmu = JSONObject.parseObject(message, Danmu.class);
                    danmu.setUserId(userId);
                    danmu.setCreateTime(new Date());
                    DanmuService danmuService = (DanmuService) APPLICATION_CONTEXT.getBean("danmuService");
                    // TODO 可以使用消息队列进行优化
                    danmuService.asyncAddDanmu(danmu);
                    //保存弹幕到redis
                    danmuService.addDanmusToRedis(danmu);
                }
            } catch (Exception e) {
                log.error("弹幕接收出现问题");
                e.printStackTrace();
            }
        }
    }

    @OnError // @OnError表示当发生异常时要调用的方法
    public void onError(Throwable error) {
    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    //设置定时任务，每5秒向前端发送当前的在线人数（或直接指定时间间隔，例如：5秒）
    @Scheduled(fixedRate = 5000)
    private void noticeOnlineCount() throws IOException {
        for (Map.Entry<String, WebSocketService> entry : WebSocketService.WEBSOCKET_MAP.entrySet()) {
            WebSocketService webSocketService = entry.getValue();
            if (webSocketService.session.isOpen()) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("onlineCount", ONLINE_COUNT.get());
                jsonObject.put("msg", "当前在线人数为" + ONLINE_COUNT.get());
                webSocketService.sendMessage(jsonObject.toJSONString());
            }
        }
    }

    public Session getSession() {
        return session;
    }

    public String getSessionId() {
        return sessionId;
    }
}
